package main

import (
	"bufio"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"strconv"
)

type selpgArgs struct {
	startPage  int
	endPage    int
	infileName string
	dest       string
	pageLen    int
	pageType   int
}

var sa selpgArgs
var progName string
var argCount int

func processArgs(args []string) {
	if len(args) < 3 {
		fmt.Fprintf(os.Stderr, "%s: not enough arguments\n", progName)
		os.Exit(1)
	}

	if args[1][0] != '-' || args[1][1] != 's' {
		fmt.Fprintf(os.Stderr, "%s: 1st arg should be -sstart_page\n", progName)
		os.Exit(1)
	}

	sp, _ := strconv.Atoi(args[1][2:])
	if sp < 1 {
		fmt.Fprintf(os.Stderr, "%s: invalid start page %d\n", progName, sp)
		os.Exit(1)
	}
	sa.startPage = sp

	if args[2][0] != '-' || args[2][1] != 'e' {
		fmt.Fprintf(os.Stderr, "%s: 2nd arg should be -eend_page\n", progName)
		os.Exit(1)
	}

	ep, _ := strconv.Atoi(args[2][2:])
	if ep < 1 || ep < sp {
		fmt.Fprintf(os.Stderr, "%s: invalid end page %d\n", progName, ep)
		os.Exit(1)
	}
	sa.endPage = ep

	argindex := 3
	for {
		if argindex > argCount-1 || args[argindex][0] != '-' {
			break
		}
		switch args[argindex][1] {
		case 'l':
			pl, _ := strconv.Atoi(args[argindex][2:])
			if pl < 1 {
				fmt.Fprintf(os.Stderr, "%s: invalid page length %d\n", progName, pl)
				os.Exit(1)
			}
			sa.pageLen = pl
			argindex++
		case 'f':
			if len(args[argindex]) > 2 {
				fmt.Fprintf(os.Stderr, "%s: option should be \"-f\"\n", progName)
				os.Exit(1)
			}
			sa.pageType = 'f'
			argindex++
		case 'd':
			if len(args[argindex]) <= 2 {
				fmt.Fprintf(os.Stderr, "%s: -d option requires a printer destination\n", progName)
				os.Exit(1)
			}
			sa.dest = args[argindex][2:]
			argindex++
		default:
			fmt.Fprintf(os.Stderr, "%s: unknown option", progName)
			os.Exit(1)
		}
	}

	if argindex <= argCount-1 {
		sa.infileName = args[argindex]
	}
}

func processInput() {
	var cmd *exec.Cmd
	var cmdI io.WriteCloser
	var cmdO io.ReadCloser
	if sa.dest != "" {
		cmd = exec.Command("bash", "-c", sa.dest)
		cmdI, _ = cmd.StdinPipe()
		cmdO, _ = cmd.StdoutPipe()
		cmd.Start()
	}
	if sa.infileName != "" {
		inf, err := os.Open(sa.infileName)
		if err != nil {
			fmt.Println(err)
			os.Exit(1)
		}
		lineNum := 1
		pageNum := 1
		fin := bufio.NewReader(inf)
		for {
			line, _, err := fin.ReadLine()
			if err != io.EOF && err != nil {
				fmt.Println(err)
				os.Exit(1)
			}
			if err == io.EOF {
				break
			}
			if pageNum >= sa.startPage && pageNum <= sa.endPage {
				if sa.dest == "" {
					fmt.Println(string(line))
				} else {
					fmt.Fprintln(cmdI, string(line))
				}
			}
			lineNum++
			if sa.pageType == 'l' {
				if lineNum > sa.pageLen {
					lineNum = 1
					pageNum++
				}
			} else {
				if string(line) == "\f" {
					pageNum++
				}
			}
		}
		if sa.dest != "" {
			cmdI.Close()
			cmdBytes, err := ioutil.ReadAll(cmdO)
			if err != nil {
				fmt.Println(err)
			}
			fmt.Print(string(cmdBytes))
			cmd.Wait()
		}
	} else {
		ns := bufio.NewScanner(os.Stdin)
		lineNum := 1
		pageNum := 1
		out := ""

		for ns.Scan() {
			line := ns.Text()
			line += "\n"
			if pageNum >= sa.startPage && pageNum <= sa.endPage {
				out += line
			}
			lineNum++
			if sa.pageType == 'l' {
				if lineNum > sa.pageLen {
					lineNum = 1
					pageNum++
				}
			} else {
				if string(line) == "\f" {
					pageNum++
				}
			}
		}
		if sa.dest == "" {
			fmt.Print(out)
		} else {
			fmt.Fprint(cmdI, out)
			cmdI.Close()
			cmdBytes, err := ioutil.ReadAll(cmdO)
			if err != nil {
				fmt.Println(err)
			}
			fmt.Print(string(cmdBytes))
			cmd.Wait()
		}
	}
}

func main() {
	args := os.Args
	sa.startPage = 1
	sa.endPage = 1
	sa.infileName = ""
	sa.dest = ""
	sa.pageLen = 20
	sa.pageType = 'l'
	argCount = len(args)
	processArgs(args)
	processInput()
}
